package com.spring.statemachine.template;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * @author mayixiang
 * @date 2021-02-19 9:44
 */
@Slf4j
public class StateMachine implements Runnable{

    private static final String ID_START = "start";
    private static final String ID_END = "end";

    private final Map<String, String> actionResultMap;
    private Map<String, ProcessTask> processTaskMap;
    private Set<ProcessTask> action;
    private ProcessTask start;
    private ProcessTask end;
    private Map<String, Object> param;
    private int productNum;
    private long tradedate;

    public int getProductNum() {
        return productNum;
    }

    public long getTradedate() {
        return tradedate;
    }

    public Object getParam(String paramKey) {
        if (CollectionUtils.isEmpty(param)){
            return null;
        }
        return param.get(paramKey);
    }

    public StateMachine(Map<String, ProcessTask> processTaskMap, int productNum, long tradedate, Map<String, Object> param){
        this.processTaskMap = processTaskMap;
        ProcessTask startTask = processTaskMap.get(ID_START);
        ProcessTask endTask = processTaskMap.get(ID_END);
        if (startTask == null){
            throw new RuntimeException(String.format("缺少 %s Task", ID_START));
        }
        if (endTask == null){
            throw new RuntimeException(String.format("缺少 %s Task", ID_END));
        }
        this.start = startTask;
        this.action = new HashSet<>();
        this.action.add(this.start);
        this.end = endTask;
        this.actionResultMap = new HashMap<>();
        this.productNum = productNum;
        this.tradedate = tradedate;
    }

    @Override public void run() {
        try{
            do {
                checkScene(this.action, this.actionResultMap);
                List<Target> targetList = new ArrayList<>();
                if (action.size() <= 1) {
                    // 串行任务执行
                    for (ProcessTask processTask : action) {
                        AbstractAction handler = processTask.getHandler();
                        actionResultMap.put(processTask.getId(), handler.action());
                        targetList.addAll(processTask.getTarget());
                    }
                } else {
                    // 并行任务执行
                    Map<String, Future<String>> futureMap = new HashMap<>();
                    for (ProcessTask processTask : action) {
                        CompletableFuture<String>
                            stringCompletableFuture =
                            CompletableFuture.supplyAsync(() -> processTask.getHandler().action());
                        futureMap.put(processTask.getId(), stringCompletableFuture);
                    }
                    for (String processId : futureMap.keySet()) {
                        try {
                            actionResultMap.put(processId, futureMap.get(processId).get());
                        } catch (InterruptedException | ExecutionException e) {
                            e.printStackTrace();
                        }
                    }
                    for (ProcessTask processTask : action) {
                        targetList.addAll(processTask.getTarget());
                    }
                }
                this.action = searchNextProcessTasks(targetList);
            } while (!CollectionUtils.isEmpty(action));
        } catch (Exception e){
            log.error("异常了", e);
        }
    }

    /**
     * 查找下一个任务
     * @param targetList 上一个任务的Target
     * @return 下一个ProcessTask
     */
    private Set<ProcessTask> searchNextProcessTasks(List<Target> targetList) {
        // 重置action
        Set<ProcessTask> processTaskSet = new HashSet<>();
        for (Target target : targetList) {
            processTaskSet.add(processTaskMap.get(target.getTargetRef()));
        }
        return processTaskSet;
    }

    /**
     * 场景检查，不符合条件的ProcessTask将被移除
     * @param action 活动
     */
    private void checkScene(Set<ProcessTask> action, Map<String, String> actionResultMap) {
        Iterator<ProcessTask> iterator = action.iterator();
        while (iterator.hasNext()) {
            ProcessTask processTask = iterator.next();
            List<Target> checkScene = processTask.getCheckScene();
            boolean pass = true;
            for (Target target : checkScene) {
                String actionResult = actionResultMap.get(target.getTargetRef());
                if (!target.getReturnEnum().equalsIgnoreCase(actionResult)) {
                    pass = false;
                }
            }
            if (!pass) {
                iterator.remove();
            }
        }
    }
}
